
/*
 * DEPENDENCIES
 */
let net = eval('require')('net'),
   SMB2Forge = require('./smb2-forge'),
   SMB2Request = SMB2Forge.request
  ;


/*
 * CONNECTION MANAGER
 */
const SMB2Connection = module.exports = {};


/*
 * CLOSE CONNECTION
 */
SMB2Connection.close = function (connection) {
  clearAutoCloseTimeout(connection);
  if (connection.connected) {
    connection.connected = false;
    connection.socket.end();
  }
};


/*
 * OPEN CONNECTION
 */
SMB2Connection.requireConnect = function (method) {
  return function () {
    const connection = this;
    const args = Array.prototype.slice.call(arguments);
    connect(connection, (err) => {
      // process the cb
      let cb = args.pop();
      cb = scheduleAutoClose(connection, cb);
      args.push(cb);

      // manage the connection error
      if (err) cb(err);
      else method.apply(connection, args);
    });
  };
};


/*
 * INIT CONNECTION
 */
SMB2Connection.init = function (connection) {
  // create a socket
  connection.connected = false;
  connection.socket = new net.Socket({
    allowHalfOpen: true,
  });

  // attach data events to socket
  connection.socket.on('data', SMB2Forge.response(connection));
  connection.errorHandler = [];
  connection.socket.on('error', function (err) {
    if (connection.errorHandler.length > 0) {
      connection.errorHandler[0].call(null, err);
    }
    if (connection.debug) {
      console.log('-- error');
      console.log(arguments);
    }
  });
};


/*
 * PRIVATE FUNCTION TO HANDLE CONNECTION
 */
function connect(connection, cb) {
  if (connection.connected) {
    cb && cb(null);
    return;
  }

  cb = scheduleAutoClose(connection, cb);

  // open TCP socket
  connection.socket.connect(connection.port, connection.ip);

  // SMB2 negotiate connection
  SMB2Request('negotiate', {}, connection, (err) => {
    if (err) cb && cb(err);
    // SMB2 setup session / negotiate ntlm
    else {
 SMB2Request('session_setup_step1', {}, connection, (err) => {
      if (err) cb && cb(err);
      // SMB2 setup session / autheticate with ntlm
      else {
 SMB2Request('session_setup_step2', {}, connection, (err) => {
        if (err) cb && cb(err);
        // SMB2 tree connect
        else {
SMB2Request('tree_connect', {}, connection, (err) => {
          if (err) cb && cb(err);
          else {
            connection.connected = true;
            cb && cb(null);
          }
        });
}
      });
}
    });
}
  });
}


/*
 * PRIVATE FUNCTION TO HANDLE CLOSING THE CONNECTION
 */
function clearAutoCloseTimeout(connection) {
  if (connection.scheduledAutoClose) {
    clearTimeout(connection.scheduledAutoClose);
    connection.scheduledAutoClose = null;
  }
}
function setAutoCloseTimeout(connection) {
  clearAutoCloseTimeout(connection);
  if (connection.autoCloseTimeout != 0) {
    connection.scheduledAutoClose = setTimeout(() => {
      connection.close();
    }, connection.autoCloseTimeout);
  }
}
function scheduleAutoClose(connection, cb) {
  addErrorListener(connection, cb);
  clearAutoCloseTimeout(connection);
  return function () {
    removeErrorListener(connection);
    setAutoCloseTimeout(connection);
    cb(...arguments);
  };
}


/*
 * PRIVATE FUNCTIONS TO HANDLE ERRORS
 */
function addErrorListener(connection, callback) {
  connection.errorHandler.unshift(callback);
}
function removeErrorListener(connection) {
  connection.errorHandler.shift();
}

